package com.bigdata.testutil;

import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * XorShift - provides a pseudo random number generator without synchronization
 * which is used for unit tests in which we do not want to introduce side
 * effects from synchronization by the test harness on the object under test.
 * 
 * @author Brian Goetz and Tim Peierls
 * @author Bryan Thompson
 */
public class XorShift {

    private static final AtomicInteger seq = new AtomicInteger(8862213);

    private int x = -1831433054;

    public XorShift(final int seed) {
        x = seed;
    }

    public XorShift() {
        this((int) System.nanoTime() + seq.getAndAdd(129));
    }

    public int next() {
        x ^= x << 6;
        x ^= x >>> 21;
        x ^= (x << 7);
        return x;
    }

    /** For compatibility with {@link Random#nextInt()}. */
    public int nextInt() {

        return next();
        
    }
    
    /** For compatibility with {@link Random#nextBoolean()}. */
    public boolean nextBoolean() {
        
        final int b = next();

        /*
         * mask a bit and test for non-zero. this uses bit ONE which tends to
         * produce true and false with a uniform distribution as demonstrated by
         * the main() routine.
         */
        
        return (b & 1/* mask */) != 0;
        
    }
    
    /** For compatibility with {@link Random#nextFloat()}. */
    public float nextFloat() {
        
        return Float.intBitsToFloat(next());
        
    }

    /**
     * Utility for looking at various distributions generated by
     * {@link XorShift}.
     * 
     * @param args
     */
    public static void main(final String[] args) {

        final XorShift r = new XorShift();
//        final Random r = new Random();
        
        final int ntrials = 1000;
        
        {
            int ntrue = 0;
            for (int i = 0; i < ntrials; i++) {
                if (r.nextBoolean())
                    ntrue++;
            }
            System.out.println("ntrials=" + ntrials + ", ntrue=" + ntrue);
        }

        {
            /*
             * Generate random values and take their running sum.
             * 
             * Note: This does not check for overflow, but it uses long for the
             * sum and int for the random values.
             */
            long sum = 0;
            final int[] a = new int[ntrials];
            for (int i = 0; i < ntrials; i++) {
                final int n = r.nextInt();
                a[i] = n;
                sum += n;
            }

            // The mean of those random values.
            final double mean = sum / (double) ntrials;

            /*
             * Compute the sum of squares of the difference between the random
             * values and their mean.
             */
            double sse = 0; // sum squared error.
            final double[] diffs = new double[ntrials];
            for (int i = 0; i < ntrials; i++) {
                double d = (mean - (double) a[i]); // difference from mean
                d *= d;// squared
                diffs[i] = d;
                sse += d;
            }
            final double var = sse / ntrials; // variance.
            final double stdev = Math.sqrt(var); // standard deviation.
            System.out.println("ntrials=" + ntrials + ", sum=" + sum + ", sse="
                    + sse + ", var=" + var + ", stdev=" + stdev);
        }

    }

}
